package com.freejy.ecommunity.web.handler;

import com.freejy.ecommunity.common.exception.BadCredentialsException;
import com.freejy.ecommunity.common.exception.DisabledException;
import com.freejy.ecommunity.common.exception.LockedException;
import com.freejy.ecommunity.common.exception.TokenException;
import com.freejy.ecommunity.utils.Result;
import com.freejy.ecommunity.utils.ResultCode;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import org.springframework.web.util.NestedServletException;

import javax.servlet.ServletException;

@ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler 
{ 
	
	/**
	 * Customize the response for NoSuchRequestHandlingMethodException.
	 * <p>This method logs a warning and delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleNoSuchRequestHandlingMethod(NoSuchRequestHandlingMethodException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.NOT_FOUND), headers, status, request);
	}
	
	/**
	 * Customize the response for MissingPathVariableException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 * @since 4.2
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleMissingPathVariable(MissingPathVariableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for MissingServletRequestParameterException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for ServletRequestBindingException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for ConversionNotSupportedException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleConversionNotSupported(ConversionNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, new Result(ResultCode.API_ERROR), headers, status, request);
	}
	
	/**
	 * Customize the response for TypeMismatchException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, new Result(ResultCode.API_ERROR), headers, status, request);
	}
	
	/**
	 * Customize the response for HttpMessageNotReadableException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for HttpMessageNotWritableException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleHttpMessageNotWritable(HttpMessageNotWritableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, new Result(ResultCode.API_ERROR), headers, status, request);
	}
	
	/**
	 * Customize the response for MethodArgumentNotValidException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for MissingServletRequestPartException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleMissingServletRequestPart(MissingServletRequestPartException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

		return handleExceptionInternal(ex, new Result(ResultCode.INVALID), headers, status, request);
	}
	
	/**
	 * Customize the response for BindException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, new Result(ResultCode.API_ERROR), headers, status, request);
	}
	
	/**
	 * Customize the response for NoHandlerFoundException.
	 * <p>This method delegates to {@link #handleExceptionInternal}.
	 * @param ex the exception
	 * @param headers the headers to be written to the response
	 * @param status the selected response status
	 * @param request the current request
	 * @return a {@code ResponseEntity} instance
	 * @since 4.0
	 */
	@SuppressWarnings("rawtypes")
	protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
		return handleExceptionInternal(ex, new Result(ResultCode.UNKNOW_ERROR), headers, status, request);
	}
	
    @SuppressWarnings("rawtypes")
	@ExceptionHandler(TokenException.class)
    public ResponseEntity<?> handleTokenException(TokenException ex, WebRequest request) 
    {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
        return handleExceptionInternal(ex, new Result(ResultCode.TOKEN_INVALID), headers, HttpStatus.BAD_REQUEST, request);
    }
    
    
    @SuppressWarnings("rawtypes")
    @ExceptionHandler(value = { RuntimeException.class, IllegalArgumentException.class, IllegalStateException.class, NullPointerException.class, ServletException.class, NestedServletException.class, NoClassDefFoundError.class })
    public ResponseEntity<?> handleRunTimeException(Exception ex, WebRequest request) 
    {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
    	Result result;
    	if (ex instanceof BadCredentialsException)
    	{
    		result = new Result(ResultCode.ACCOUNT_ERROR);
    	} else if(ex instanceof DisabledException)
    	{
    		result = new Result(ResultCode.INVALID_ACCOUNT);
    	} else if(ex instanceof LockedException)
    	{
    		result = new Result(ResultCode.INVALID_ACCOUNT);
    	}else 
    	{
    		result = new Result(ResultCode.API_ERROR);
    	}
    	return handleExceptionInternal(ex, result, headers, HttpStatus.BAD_REQUEST, request);
    }   

    @SuppressWarnings("rawtypes")
	@ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleAllException(Exception ex, WebRequest request) 
    {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
    	return handleExceptionInternal(ex, new Result(ResultCode.API_ERROR), headers, HttpStatus.BAD_REQUEST, request);
    }
    
}